<?php
/*
 * Copyright (c) 2009 Tom Smith (tom@takeontom.com).
 * 
 * Distributed under the terms of the GNU Lesser General Public License.
 * 
 * ---------------------------------------------------------------------------------
 * 
 * This file is part of Seraph PHP Framework.
 *
 * Seraph PHP Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Seraph PHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Seraph PHP Framework.  If not, see <http://www.gnu.org/licenses/>.
*/


abstract class DBConnection	{
	protected $connection;
	
	protected $host;
	protected $user;
	protected $pass;
	protected $schema;
	
	protected $connectError;
	
	protected $queryCount = 0;
	
	public function __construct($host, $user, $pass, $schema)	{
		$this->host = $host;
		$this->user = $user;
		$this->pass = $pass;
		$this->schema = $schema;
	}
	
	/**
	 * Set the reference to the connection. This can be whatever the main connection resource is for
	 * the implemented db/driver/extension type. e.g. for vanilla mySQL extension this will be a
	 * resource value. For mySQLi this will be an actual oject.
	 * 
	 * @param $connection
	 * @return unknown_type
	 */
	public function setConnection($connection)	{
		$this->connection = $connection;
	}
	/**
	 * Get the connection object.
	 * 
	 * Returned value will vary depending on database/driver/extension type
	 * 
	 * @return variant
	 */
	public function getConnection()	{
		if (!isset ($this->connection))	{
			$res = $this->doConnect();
			if ($res !== true && $res !== false)	{
				throw new UnexpectedValueException('doConnect did not return a valid boolean');
			}
		}
		return $this->connection;
	}
	
	/**
	 * Run a single SQL query.
	 * 
	 * Will return a result object containing any results and other information
	 * about the query.
	 * 
	 * @param $SQL
	 * @return DBResult
	 */
	public function query($SQL)	{
		$this->queryCount++;
		
		$result = $this->makeNewResultObject();
		
		$result->start();
		$result->setSQL($SQL);
		
		$this->executeSQL($SQL, $result);
		$result->stop();
		
		return $result;
	}
	
	/**
	 * @return int
	 */
	public function getQueryCount()	{
		return $this->queryCount;
	}
	
	/**
	 * Generates the SQL for an insert statement for the given table and data.
	 * 
	 * @param $tableName
	 * @param $data
	 * @return string
	 */
	public function generateInsertStatementSQL($tableName, array $data)	{
		$sqlFormat = '
INSERT INTO `%1$s`
(%2$s)
VALUES
(%3$s)
;
';	
		$columnNames = implode(',', array_keys($data));
		
		foreach($data as $aData)	{
			if (!isset($values))	{
				$values = '';
			} else {
				$values .= ', ';
			}
			
			$values .= '\'' . $this->escapeString($aData) . '\'';
		}
		
		$sql = sprintf($sqlFormat, $tableName, $columnNames, $values);
		return $sql;
	}
	
	/**
	 * @param $tableName
	 * @param $data
	 * @param $where
	 * @return string
	 */
	public function generateUpdateStatementSQL($tableName, array $data, array $where = array())	{
		$sqlFormat = '
UPDATE `%1$s`
SET
%2$s
%3$s
';
		foreach($data as $col => $value)	{
			if (!isset($valSQL))	{
				$valSQL = '';
			} else {
				$valSQL .= ', ';
			}
			$valSQL .= $col . ' = \'' . $this->escapeString($value) . '\'';
		}
		
		$whereSQL = $this->generateWhereSQLClause($where);
		
		$sql = sprintf($sqlFormat, $tableName, $valSQL, $whereSQL);
		return $sql;
	}
	
	public function generateDeleteStatementSQL($tableName, array $where = array())	{
		$sqlFormat = '
DELETE FROM `%1$s`
%2$s
';
		$whereSQL = $this->generateWhereSQLClause($where);
		$sql = sprintf($sqlFormat, $tableName, $whereSQL);
		return $sql;
	}
	
	public function generateSelectSQLStatement($tableName, array $where = array())	{
		$sqlFormat = '
SELECT *
FROM `%1$s`
%2$s
;';
		$sql = sprintf($sqlFormat, $this->escapeString($tableName), $this->generateWhereSQLClause($where));
		return $sql;
	}
	
	public function generateWhereSQLClause(array $where)	{
		if (count($where) > 0)	{
			foreach($where as $col => $value)	{
				if (!isset($whereSQL))	{
					$whereSQL = 'WHERE ';
				} else {
					$whereSQL .= ' AND ';
				}
				$whereSQL .= '`' . $col . '`' . ' = \'' . $this->escapeString($value) . '\'';
				}
		} else {
			$whereSQL = '';
		}
		return $whereSQL;
	}
	
	/**
	 * Executes an SQL statement, placing the result and other query information
	 * into the supplied result object.
	 * 
	 * @param $SQL
	 * @param $result
	 */
	abstract function executeSQL($SQL, DBResult $result);
	
	/**
	 * Creates a new result object for this database type.
	 * @return DBResult
	 */
	abstract function makeNewResultObject();
	
	/**
	 * Get the last inserted id
	 * @return int
	 */
	abstract public function getLastInsertID();
	
	/**
	 * Escapes the supplied string and returns the result.
	 * 
	 * @param $string
	 * @return string
	 */
	abstract public function escapeString($string);
	
	/**
	 * Initiate a connection to the database.
	 * 
	 * Will return bool on success/failure
	 * 
	 * @return bool
	 */
	abstract public function doConnect();
	
	/**
	 * Retrieves the last connection error.
	 * @return string
	 */
	abstract public function getConnectError();
	
	/**
	 * Disconnect from the database.
	 * 
	 * @return bool
	 */
	abstract public function doDisconnect();
	
	/**
	 * Returns an array containing the names of all tables in the database.
	 * 
	 * If no tables exist, will return an empty array.
	 * 
	 * @return array
	 */
	abstract public function getTableNames();
}
?>